package repository

import (
	"context"
	"microBook/internal/domain"
	"microBook/internal/repository/cache"
	"microBook/internal/repository/dao"
)

var (
	ErrUserDuplicateEmail = dao.ErrUserDuplicateEmail
	ErrUserNotFound       = dao.ErrUserNotFound
)

type UserRepository struct {
	dao *dao.UserDAO
	c   *cache.UserCache
}

func NewUserRepository(dao *dao.UserDAO, c *cache.UserCache) *UserRepository {
	return &UserRepository{
		dao: dao,
		c:   c,
	}
}

// Create 注册
func (r *UserRepository) Create(ctx context.Context, user domain.User) error {
	return r.dao.Insert(
		ctx, dao.User{
			Email:    user.Email,
			Password: user.Password,
		},
	)
}

// FindByEmail 利用email查找
func (r *UserRepository) FindByEmail(ctx context.Context, email string) (domain.User, error) {
	u, err := r.dao.FindByEmail(ctx, email)
	if err != nil {
		return domain.User{}, err
	}
	return domain.User{
		Id:       u.Id,
		Email:    u.Email,
		Password: u.Password}, nil
}

// FindById 利用ID查找
func (r UserRepository) FindById(ctx context.Context, id uint64) (domain.User, error) {
	// 先去缓存找
	u, err := r.c.Get(ctx, id)
	if err == nil {
		return u, err
	}

	// Redis 找不到就去数据库找
	ue, err := r.dao.FindById(ctx, id)
	if err != nil {
		return domain.User{}, err
	}
	u = domain.User{
		Id:       ue.Id,
		Email:    ue.Email,
		Password: ue.Password,
	}
	go func() {
		err = r.c.Set(ctx, u)
		if err != nil {
			// 打日志，做监控就好
			//return domain.User{}, err
		}
	}()

	// 这是 Redis 出错之后不查数据库的写法
	/*
		switch err {
		case nil:
			return u, err
		case cache.ErrKeyNotExist:
			ue, err := r.dao.FindById(ctx, id)
			if err != nil {
				return domain.User{}, err
			}
			u = domain.User{
				Id:       ue.Id,
				Email:    ue.Email,
				Password: ue.Password,
			}
		default:
			return domain.User{}, err
		}
		/*
	*/
	//if err == cache.ErrKeyNotExist {
	//	// 去数据库找
	//}
	// 这里怎么办？ err = io.EOF
	// 要不要数据库加载

	// 选加载 --做好兜底，万一 Redis真的崩了，要保护数据库
	// 数据库限流

	// 选不加载 -- 用户体验差一点

	return u, err
}
